我用Facebook开源神器Prophet,预测股市行情基于Python(系列2)
本期作者:Eric Brown
本期编辑:Allen | 崙
系列1:我用Facebook开源神器Prophet,预测时间序列基于Python
数据基于标普500指数:
import pandas as pd
import numpy as np
from fbprophet import Prophet
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize']=(20,10)
plt.style.use('ggplot')
market_df = pd.read_csv('../examples/SP500.csv', index_col='DATE', parse_dates=True)
df = market_df.reset_index().rename(columns={'DATE':'ds', 'SP500':'y'})
df['y'] = np.log(df['y'])
#lets take a look at our data quickly
df.set_index('ds').y.plot()
运行Prophet
model = Prophet()
model.fit(df);
future = model.make_future_dataframe(periods=366)
forecast = model.predict(future)
Prophet已经创建了所需的模型并匹配数据。Prophet在默认情况下为我们创建了变化点并将它们存储在.changepoints中。默认情况下,Prophet在初始数据集的80%中添加了25个变化点。在初始化prophet时,可以使用n_changepoints参数更改点的数量(例如,model= prophet (n_changepoints=30))
model.changepoints
除了查看变化点的日期之外,我们还可以查看添加了变化点的图表:
figure = model.plot(forecast)
for changepoint in model.changepoints:
plt.axvline(changepoint,ls='--', lw=1)
看看上面图表中可能的变化点,我们可以看到它们与一些高点和低点非常吻合。你还可以使用以下代码查看这个可视化:
deltas = model.params['delta'].mean(0)
fig = plt.figure(facecolor='w')
ax = fig.add_subplot(111)
ax.bar(range(len(deltas)), deltas)
ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
ax.set_ylabel('Rate change')
ax.set_xlabel('Potential changepoint')
fig.tight_layout()
从上面的图表中我们可以看到,有相当多的变化点在10到20之间,它们的幅度非常小,在预测中最容易被忽视。
现在,如果我们知道过去的趋势发生了什么变化,我们可以将这些已知的变化点添加到dataframe中。对于这些数据,注:事实上,低或高并不意味着它是一个真正的变化点或趋势变化,但让我们假设它是。
m = Prophet(changepoints=['2009-03-09', '2010-07-02', '2011-09-26', '2012-03-20', '2010-04-06'])
forecast = m.fit(df).predict(future)
m.plot(forecast)
figure = m.plot(forecast)
for changepoint in m.changepoints:
plt.axvline(changepoint,ls='--', lw=1)
我们可以看到,通过手动设置我们的变化点与使用自动检测变化点相比,我们对模型进行了巨大的更改。除非你非常确定过去的趋势变化点,最好使用Prophet提供的默认值。
Prophet对趋势变化点的使用是非常棒的,特别是那些信号/数据集在信号的生命周期中有显著的变化。也就是说,除非你能确定你的变化点,否则最好让Prophet自动去完成。
预测市场
还是使用标普500指数:
df = market_df.reset_index().rename(columns={'DATE':'ds', 'SP500':'y'})
df['y'] = np.log(df['y'])
model = Prophet()
model.fit(df);
future = model.make_future_dataframe(periods=365) #forecasting for 1 year from now.
forecast = model.predict(future)
figure=model.plot(forecast)
根据现有的数据,很难看出预测(蓝线)和实际数据(黑点)的关系。让我们看一下过去800个数据点(大约2年)的预测和实际预测,而不看未来的预测:
two_years = forecast.set_index('ds').join(market_df)
two_years = two_years[['SP500', 'yhat', 'yhat_upper', 'yhat_lower' ]].dropna().tail(800)
two_years['yhat']=np.exp(two_years.yhat)
two_years['yhat_upper']=np.exp(two_years.yhat_upper)
two_years['yhat_lower']=np.exp(two_years.yhat_lower)
two_years[['SP500', 'yhat']].plot()
你可以从上面的图表中看到,预测很好地遵循了趋势,但似乎不太擅长捕捉市场的“波动”。如果我们对“顺应趋势”,而不是试图完美地把握高峰和低谷,这对我们来说可能是件好事。
让我们来看看一些测量准确度的方法:
two_years_AE = (two_years.yhat - two_years.SP500)
print two_years_AE.describe()
count 800.000000
mean -0.540173
std 47.568987
min -141.265774
25% -29.383549
50% -1.548716
75% 25.878416
max 168.898459
dtype: float64
再来看看一些更精确的测量方法。使用sklearn的r2_score函数的R-squared:
r2_score(two_years.SP500, two_years.yhat)
0.90563333683064451
R-squared看起来不错,在任何第一轮建模中,都会取0.9。
现在我们来看看均值平方误差:
mean_squared_error(two_years.SP500, two_years.yhat)
2260.2718233576029
现在,让我们看看平均绝对误差(MAE)。能让我们更好地了解错误率,而不是标准平均值。
mean_absolute_error(two_years.SP500, two_years.yhat)
36.179476001483771
MAE还告诉我们,通过Prophet预测交易并不理想。
另一种查看该预测有效性的方法是根据实际值绘制预测的上置信区间和下置信区间。可以通过绘制yhat_upper和yhat_lower来实现。
fig, ax1 = plt.subplots()
ax1.plot(two_years.SP500)
ax1.plot(two_years.yhat)
ax1.plot(two_years.yhat_upper, color='black', linestyle=':', alpha=0.5)
ax1.plot(two_years.yhat_lower, color='black', linestyle=':', alpha=0.5)
ax1.set_title('Actual S&P 500 (Orange) vs S&P 500 Forecasted Upper & Lower Confidence (Black)')
ax1.set_ylabel('Price')
ax1.set_xlabel('Date')
你不能从这张图表中看出任何可以量化的东西,但你可以对预测的价值做出判断。如果你想做短线交易(1天到几周),这个预测几乎是无用的,但是如果你的投资时间是几个月到几年,这个预测可能会提供一些价值,以更好地了解市场的趋势和预测的趋势。
让我们回过头来看看实际的预测,看看它是否能告诉我们一些与预测和实际数据不同的东西。
fig, ax1 = plt.subplots()
ax1.plot(full_df.SP500)
ax1.plot(full_df.yhat, color='black', linestyle=':')
ax1.fill_between(full_df.index, np.exp(full_df['yhat_upper']), np.exp(full_df['yhat_lower']), alpha=0.5, color='darkgray')
ax1.set_title('Actual S&P 500 (Orange) vs S&P 500 Forecasted (Black) with Confidence Bands')
ax1.set_ylabel('Price')
ax1.set_xlabel('Date')
L=ax1.legend()
L.get_texts()[0].set_text('S&P 500 Actual')
L.get_texts()[1].set_text('S&P 5600 Forecasted')
这个图比默认的Prophet表图容易理解。我们可以看到,在实际价值和预测的历史中,Prophet做了一个不错的预测但是在市场变得非常不稳定的时候,它的表现很一般。
具体看一下未来的预测,Prophe告诉我们市场将继续上升,在预测期结束时应该在2750左右,区间从2000到4000左右。也许我们可以更准确地使用每周或每月的数据预测。
推荐阅读